﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Pixysoft.Framework.Configurations;
using System.IO;

namespace Pixysoft.Framework.Noebe.MemoryCaches.Core
{
    class CacheDataSet : ICacheDataSet
    {
        List<CacheDataTable> tables = new List<CacheDataTable>();

        private static volatile CacheDataSet instance;

        private static object syncRoot = new Object();

        public static CacheDataSet Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        instance = new CacheDataSet();
                    }
                }

                return instance;

            }
        }


        public CacheDataSet()
        {
        }


        public bool Contains(string settingName, string sqlValue)
        {
            return GetCache(settingName, sqlValue) != null;
        }

        public bool Contains(string settingName, string sqlValue, string key)
        {
            ICacheDataTable table = GetCache(settingName, sqlValue);

            if (table == null)
                return false;

            return table.Contains(key);
        }


        public ICacheDataTable CreateCache(string settingName, string sqlValue, DataTable schema)
        {
            return CreateCache(settingName, sqlValue, schema, StringHelper.default_cache_maxcapacity);
        }

        public ICacheDataTable CreateCache(string settingName, string sqlValue, DataTable schema, int capacity)
        {
            if (Contains(settingName, sqlValue))
            {
                LoggerHelper.Info("duplicate datatable existed in cache, remove it. settingname:{0},sqlvalue:{1}",
                    settingName, sqlValue);

                CacheDataTable removetable = GetCache(settingName, sqlValue) as CacheDataTable;

                if (removetable != null)
                    tables.Remove(removetable);
            }

            string cachetablename = GetCacheTableName(settingName, sqlValue);

            CacheDataTable table = new CacheDataTable(capacity, cachetablename, schema);

            tables.Add(table);

            return table;
        }


        public ICacheDataTable GetCache(string settingName, string sqlValue)
        {
            if (string.IsNullOrEmpty(sqlValue))
                return null;

            string cachetablename = GetCacheTableName(settingName, sqlValue);

            foreach (CacheDataTable table in tables)
            {
                if (cachetablename.Equals(table.CacheTableName, StringComparison.OrdinalIgnoreCase))
                    return table;
            }

            return null;
        }

        public DataTable GetCache(string settingName, string sqlValue, string key)
        {
            ICacheDataTable table = GetCache(settingName, sqlValue);

            if (table == null)
                throw new Exception("cache do not existed.");

            return table.Get(key);
        }

        public DataTable GetCacheTable(string tablename)
        {
            if (string.IsNullOrEmpty(tablename))
                return null;

            foreach (CacheDataTable table in tables)
            {
                if (string.Equals(table.TableName, tablename, StringComparison.OrdinalIgnoreCase))
                    return table.GetAll();
            }

            return null;

        }

        public void RemoveTable(string tablename)
        {
            if (string.IsNullOrEmpty(tablename))
                return;

            List<CacheDataTable> rlist = new List<CacheDataTable>();

            foreach (CacheDataTable table in tables)
            {
                if (string.Equals(table.TableName, tablename, StringComparison.OrdinalIgnoreCase))
                {
                    rlist.Add(table);
                }
            }

            foreach (CacheDataTable table in rlist)
            {
                tables.Remove(table);
            }
        }

        public void ClearTable(string tablename)
        {
            if (string.IsNullOrEmpty(tablename))
                return;

            foreach (CacheDataTable table in tables)
            {
                if (string.Equals(table.TableName, tablename, StringComparison.OrdinalIgnoreCase))
                {
                    table.ClearAll();
                }
            }
        }

        public void Clear(string settingName, string sqlValue)
        {
            ICacheDataTable table = GetCache(settingName, sqlValue);

            if (table == null)
                return;

            table.ClearAll();
        }

        public void Clear(string settingName, string sqlValue, string key)
        {
            ICacheDataTable table = GetCache(settingName, sqlValue);

            if (table == null)
                return;

            table.Clear(key);
        }

        public void ClearAll()
        {
            this.tables.Clear();
        }

        public bool Verify()
        {
            bool passed = true;

            List<CacheDataTable> rlist = new List<CacheDataTable>();

            foreach (CacheDataTable table in tables)
            {
                if (!table.Verify())
                    rlist.Add(table);
            }

            foreach (CacheDataTable table in rlist)
            {
                tables.Remove(table);

                LoggerHelper.Info("has invalid memory cache. tablename = " + table.TableName);

                passed = false;
            }

            return passed;


        }

        /// <summary>
        /// 被序列化的方法！不能够作为private方法
        /// </summary>
        public List<CacheDataTable> Tables
        {
            get
            {
                return tables;
            }
            set
            {
                tables = value;
            }
        }

        private string GetCacheTableName(string settingName, string sqlValue)
        {
            return Pixysoft.Security.MD5.GetMD5(settingName + sqlValue);
        }


        public static void Save()
        {
            Save(true);
        }

        public static void Save(bool encrypt)
        {
            string content = XmlManager.Serialize(Instance).Serialize(true);

            content = Pixysoft.IO.Compressor.Instance.Compress(content);

            if (encrypt)
                content = Pixysoft.Security.DES.Encrypt(content, StringHelper.global_framework_id);

            try
            {
                File.WriteAllText(
                StringFilter.RegularPath(AppDomain.CurrentDomain.BaseDirectory) + StringHelper.default_setting_filename, content);
            }
            catch (Exception ex)
            {
                LoggerHelper.Fatal(ex);
            }
        }

        public static void Load()
        {
            Load(true);
        }

        public static void Load(bool encrypt)
        {
            string path = StringFilter.RegularPath(AppDomain.CurrentDomain.BaseDirectory) +
                          StringHelper.default_setting_filename;

            if (!File.Exists(path))
                return;

            string content = null;

            try
            {
                content = File.ReadAllText(path);

                if (encrypt)
                    content = Pixysoft.Security.DES.Decrypt(content, StringHelper.global_framework_id);

                content = Pixysoft.IO.Compressor.Instance.DeCompress(content);
            }
            catch (Exception ex)
            {
                LoggerHelper.Error(ex.ToString());

                Instance.Tables = new List<CacheDataTable>();

                return;
            }

            CacheDataSet ds = null;

            try
            {
                ds = XmlManager.Deserialize<CacheDataSet>(content);

                Instance.Tables = ds.Tables;
            }
            catch (Exception ex)
            {
                LoggerHelper.Fatal(ex);
            }

            if (!ds.Verify())
            {
                Save(encrypt);
            }
        }

    }
}
